package com.cf.docker.service;

import com.cf.constant.EnumReturnCode;
import com.cf.docker.service.ssh.SSHResInfo;
import com.cf.exception.BizException;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * boot-awm-SSHService
 *
 * @author <a href="2428922347@qq.com">Zhu mingming</a>
 * @info
 * @since 2018/6/20 下午 03:44
 */
@Slf4j
public class SSHService {

    private Map<String, Session> sessions = new ConcurrentHashMap();

    private String charset = Charset.defaultCharset().toString();


    /**
     *
     */
    private void initConnection(String user, String host, String password) {
        try {
            JSch jsch = new JSch();
            Session session = jsch.getSession(user, host);
            session.setPassword(password);
            //设置第一次登陆的时候提示，可选值:(ask | yes | no)
            session.setConfig("StrictHostKeyChecking", "no");
            //30秒连接超时
            session.connect(5000);
            sessions.put(host, session);
        } catch (Exception e) {
            throw new BizException(EnumReturnCode.ERROR.getCode(), "connection初始化失败！");
        }

    }

    /**
     * 连接sftp服务器
     *
     * @param host     远程主机ip地址
     * @param user     用户名
     * @param password 密码
     * @return
     * @throws JSchException
     */
    private Session getSession(String host, String user, String password) {
        Session session = sessions.get(host);

        if (session == null)
            initConnection(user, host, password);

        session = sessions.get(host);
        if (!session.isConnected()) {
            try {
                session.connect(5000);
            } catch (JSchException e) {
                throw new BizException(EnumReturnCode.ERROR.getCode(), "ssh 连接失败！");
            }
        }
        return session;
    }


    public SSHResInfo sendCmd(String command, String host, String user, String password) throws BizException {
        Session session = getSession(host, user, password);
        return sendCmd(session, command);
    }

    public void sendCmd(String command, javax.websocket.Session webSession, String host, String user, String password) throws BizException {
        Session session = getSession(host, user, password);
        sendCmd(session, command, webSession);
    }


    private SSHResInfo sendCmd(Session session, String command) {

        Channel channel = null;
        try {
            channel = session.openChannel("exec");
            ChannelExec ssh = (ChannelExec) channel;
            //返回的结果可能是标准信息,也可能是错误信息,所以两种输出都要获取
            //一般情况下只会有一种输出.
            //但并不是说错误信息就是执行命令出错的信息,如获得远程java JDK版本就以
            //ErrStream来获得.
            InputStream stdStream = ssh.getInputStream();
            InputStream errStream = ssh.getErrStream();

            ssh.setCommand(command);
            ssh.connect();
            //开始获得SSH命令的结果
            int code = ssh.getExitStatus();
            String resultStr = "";
            resultStr += IOUtils.toString(stdStream, charset);
            resultStr += IOUtils.toString(errStream, charset);
            return new SSHResInfo(code, resultStr);
        } catch (Exception e) {
            throw new BizException(EnumReturnCode.ERROR.getCode(), "执行命令失败！");
        } finally {
            if (channel != null)
                channel.disconnect();
        }
    }

    private void sendCmd(Session session, String command, javax.websocket.Session webSession) {

        Channel channel = null;
        try {
            channel = session.openChannel("exec");
            ChannelExec ssh = (ChannelExec) channel;
            //返回的结果可能是标准信息,也可能是错误信息,所以两种输出都要获取
            //一般情况下只会有一种输出.
            //但并不是说错误信息就是执行命令出错的信息,如获得远程java JDK版本就以
            //ErrStream来获得.
            InputStream stdStream = ssh.getInputStream();
            InputStream errStream = ssh.getErrStream();

            ssh.setCommand(command);
            ssh.connect();
            //开始获得SSH命令的结果
            sendResponse(webSession, stdStream);
            sendResponse(webSession, errStream);
        } catch (Exception e) {
            throw new BizException(EnumReturnCode.ERROR.getCode(), "执行命令失败！");
        } finally {
            if (channel != null)
                channel.disconnect();
        }
    }

    private void sendResponse(javax.websocket.Session session, InputStream stdStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(stdStream));
        String str;
        while ((str = reader.readLine()) != null) {
            session.getBasicRemote().sendText(str);
        }
        reader.close();
    }


    /**
     * 重新连接ssh
     *
     * @param serverIp
     */
    public void reconnection(String serverIp) {
        //在getSession的时候会初始化连接，所以当前就不出事session了
        Session session = this.sessions.get(serverIp);
        if (session != null && session.isConnected())
            session.disconnect();
    }


    /**
     * 关闭连接
     *
     * @param ip
     */
    public void close(String ip) {
        Session session = sessions.get(ip);
        if (session != null)
            session.disconnect();
    }


    public static void main(String[] args) {
        SSHService sshService = new SSHService();
        System.out.println(sshService.sendCmd("docker run -d  -p 8082:8082 registry.cn-hangzhou.aliyuncs.com/org_zmm/boot-web:3.0", "39.104.111.71", "root", "Jiangnan77"));
    }
}
